package grpc

import (
	"context"
	"fmt"

	userv1 "github.com/devops-smartbot/devops-smartbot/proto/gen/go/user/v1"
	"github.com/devops-smartbot/devops-smartbot/services/user-service/internal/models"
	"github.com/devops-smartbot/devops-smartbot/services/user-service/internal/service"
)

type Server struct {
	userv1.UnimplementedUserServiceServer
	userService *service.UserService
}

func NewServer(userService *service.UserService) *Server {
	return &Server{
		userService: userService,
	}
}

// Register registers a new user
func (s *Server) Register(ctx context.Context, req *userv1.RegisterRequest) (*userv1.RegisterResponse, error) {
	user, accessToken, refreshToken, err := s.userService.Register(
		ctx,
		req.Username,
		req.Email,
		req.Password,
		req.FullName,
	)

	if err != nil {
		return &userv1.RegisterResponse{}, fmt.Errorf("registration failed: %w", err)
	}

	return &userv1.RegisterResponse{
		User:         userModelToProto(user),
		AccessToken:  accessToken,
		RefreshToken: refreshToken,
		ExpiresIn:    3600, // 1 hour
	}, nil
}

// Login authenticates a user
func (s *Server) Login(ctx context.Context, req *userv1.LoginRequest) (*userv1.LoginResponse, error) {
	user, accessToken, refreshToken, err := s.userService.Login(ctx, req.Username, req.Password)
	if err != nil {
		return &userv1.LoginResponse{}, fmt.Errorf("login failed: %w", err)
	}

	return &userv1.LoginResponse{
		User:         userModelToProto(user),
		AccessToken:  accessToken,
		RefreshToken: refreshToken,
		ExpiresIn:    3600, // 1 hour
	}, nil
}

// RefreshToken refreshes access token
func (s *Server) RefreshToken(ctx context.Context, req *userv1.RefreshTokenRequest) (*userv1.RefreshTokenResponse, error) {
	accessToken, refreshToken, err := s.userService.RefreshToken(ctx, req.RefreshToken)
	if err != nil {
		return &userv1.RefreshTokenResponse{}, fmt.Errorf("token refresh failed: %w", err)
	}

	return &userv1.RefreshTokenResponse{
		AccessToken:  accessToken,
		RefreshToken: refreshToken,
		ExpiresIn:    3600,
	}, nil
}

// Logout logs out a user
func (s *Server) Logout(ctx context.Context, req *userv1.LogoutRequest) (*userv1.LogoutResponse, error) {
	err := s.userService.Logout(ctx, req.UserId)
	if err != nil {
		return &userv1.LogoutResponse{
			Success: false,
			Message: err.Error(),
		}, nil
	}

	return &userv1.LogoutResponse{
		Success: true,
		Message: "Logged out successfully",
	}, nil
}

// GetUser retrieves a user
func (s *Server) GetUser(ctx context.Context, req *userv1.GetUserRequest) (*userv1.GetUserResponse, error) {
	user, err := s.userService.GetUser(ctx, req.UserId)
	if err != nil {
		return &userv1.GetUserResponse{}, err
	}

	return &userv1.GetUserResponse{
		User: userModelToProto(user),
	}, nil
}

// UpdateUser updates a user
func (s *Server) UpdateUser(ctx context.Context, req *userv1.UpdateUserRequest) (*userv1.UpdateUserResponse, error) {
	user, err := s.userService.UpdateUser(ctx, req.UserId, req.Email, req.FullName, req.AvatarUrl)
	if err != nil {
		return &userv1.UpdateUserResponse{}, err
	}

	return &userv1.UpdateUserResponse{
		User: userModelToProto(user),
	}, nil
}

// DeleteUser deletes a user
func (s *Server) DeleteUser(ctx context.Context, req *userv1.DeleteUserRequest) (*userv1.DeleteUserResponse, error) {
	err := s.userService.DeleteUser(ctx, req.UserId)
	if err != nil {
		return &userv1.DeleteUserResponse{
			Success: false,
			Message: err.Error(),
		}, nil
	}

	return &userv1.DeleteUserResponse{
		Success: true,
		Message: "User deleted successfully",
	}, nil
}

// ListUsers lists users
func (s *Server) ListUsers(ctx context.Context, req *userv1.ListUsersRequest) (*userv1.ListUsersResponse, error) {
	page := int(req.Page)
	if page < 1 {
		page = 1
	}
	pageSize := int(req.PageSize)
	if pageSize < 1 {
		pageSize = 20
	}

	role := roleProtoToModel(req.Role)
	status := statusProtoToModel(req.Status)

	users, total, err := s.userService.ListUsers(ctx, page, pageSize, role, status, req.Search)
	if err != nil {
		return &userv1.ListUsersResponse{}, err
	}

	protoUsers := make([]*userv1.User, len(users))
	for i, user := range users {
		protoUsers[i] = userModelToProto(user)
	}

	return &userv1.ListUsersResponse{
		Users: protoUsers,
		Total: int32(total),
	}, nil
}

// ChangePassword changes user password
func (s *Server) ChangePassword(ctx context.Context, req *userv1.ChangePasswordRequest) (*userv1.ChangePasswordResponse, error) {
	err := s.userService.ChangePassword(ctx, req.UserId, req.OldPassword, req.NewPassword)
	if err != nil {
		return &userv1.ChangePasswordResponse{
			Success: false,
			Message: err.Error(),
		}, nil
	}

	return &userv1.ChangePasswordResponse{
		Success: true,
		Message: "Password changed successfully",
	}, nil
}

// ResetPassword resets user password
func (s *Server) ResetPassword(ctx context.Context, req *userv1.ResetPasswordRequest) (*userv1.ResetPasswordResponse, error) {
	resetToken, err := s.userService.ResetPassword(ctx, req.Email)
	if err != nil {
		return &userv1.ResetPasswordResponse{
			Success: false,
			Message: err.Error(),
		}, nil
	}

	return &userv1.ResetPasswordResponse{
		Success:    true,
		Message:    "Password reset link sent to email",
		ResetToken: resetToken,
	}, nil
}

// AssignRole assigns a role to user
func (s *Server) AssignRole(ctx context.Context, req *userv1.AssignRoleRequest) (*userv1.AssignRoleResponse, error) {
	role := roleProtoToInternalModel(req.Role)
	user, err := s.userService.AssignRole(ctx, req.UserId, role)
	if err != nil {
		return &userv1.AssignRoleResponse{}, err
	}

	return &userv1.AssignRoleResponse{
		User: userModelToProto(user),
	}, nil
}

// GetUserPermissions gets user permissions
func (s *Server) GetUserPermissions(ctx context.Context, req *userv1.GetUserPermissionsRequest) (*userv1.GetUserPermissionsResponse, error) {
	permissions, role, err := s.userService.GetUserPermissions(ctx, req.UserId)
	if err != nil {
		return &userv1.GetUserPermissionsResponse{}, err
	}

	permStrs := make([]string, len(permissions))
	for i, p := range permissions {
		permStrs[i] = string(p)
	}

	return &userv1.GetUserPermissionsResponse{
		Permissions: permStrs,
		Role:        roleInternalModelToProto(role),
	}, nil
}

// Helper functions

func userModelToProto(user *models.User) *userv1.User {
	protoUser := &userv1.User{
		Id:            user.ID,
		Username:      user.Username,
		Email:         user.Email,
		FullName:      user.FullName,
		Role:          roleInternalModelToProto(user.Role),
		Status:        statusInternalModelToProto(user.Status),
		AvatarUrl:     user.AvatarURL,
		CreatedAtUnix: user.CreatedAt.Unix(),
		UpdatedAtUnix: user.UpdatedAt.Unix(),
	}

	if user.LastLoginAt != nil {
		protoUser.LastLoginUnix = user.LastLoginAt.Unix()
	}

	return protoUser
}

func roleProtoToModel(role userv1.Role) string {
	switch role {
	case userv1.Role_ROLE_VIEWER:
		return "viewer"
	case userv1.Role_ROLE_OPERATOR:
		return "operator"
	case userv1.Role_ROLE_ADMIN:
		return "admin"
	case userv1.Role_ROLE_SUPER_ADMIN:
		return "super_admin"
	default:
		return ""
	}
}

func roleProtoToInternalModel(role userv1.Role) models.Role {
	switch role {
	case userv1.Role_ROLE_VIEWER:
		return models.RoleViewer
	case userv1.Role_ROLE_OPERATOR:
		return models.RoleOperator
	case userv1.Role_ROLE_ADMIN:
		return models.RoleAdmin
	case userv1.Role_ROLE_SUPER_ADMIN:
		return models.RoleSuperAdmin
	default:
		return models.RoleViewer
	}
}

func roleInternalModelToProto(role models.Role) userv1.Role {
	switch role {
	case models.RoleViewer:
		return userv1.Role_ROLE_VIEWER
	case models.RoleOperator:
		return userv1.Role_ROLE_OPERATOR
	case models.RoleAdmin:
		return userv1.Role_ROLE_ADMIN
	case models.RoleSuperAdmin:
		return userv1.Role_ROLE_SUPER_ADMIN
	default:
		return userv1.Role_ROLE_UNSPECIFIED
	}
}

func statusProtoToModel(status userv1.UserStatus) string {
	switch status {
	case userv1.UserStatus_USER_STATUS_ACTIVE:
		return "active"
	case userv1.UserStatus_USER_STATUS_INACTIVE:
		return "inactive"
	case userv1.UserStatus_USER_STATUS_SUSPENDED:
		return "suspended"
	default:
		return ""
	}
}

func statusInternalModelToProto(status models.UserStatus) userv1.UserStatus {
	switch status {
	case models.UserStatusActive:
		return userv1.UserStatus_USER_STATUS_ACTIVE
	case models.UserStatusInactive:
		return userv1.UserStatus_USER_STATUS_INACTIVE
	case models.UserStatusSuspended:
		return userv1.UserStatus_USER_STATUS_SUSPENDED
	default:
		return userv1.UserStatus_USER_STATUS_UNSPECIFIED
	}
}
